1. Le patron Observer
1.1. Motivation
1.2. Définition
Observateur définit une relation entre objets de type un-à-plusieurs, de façon que, lorsqu’un objet change d’état, tous ceux qui en dépendent en soient notifiés et soient mis à jour automatiquement.
1.3. Application
Le patron Observer est utilisable dans de nombreuses situations :
-
Quand un concept a deux aspects, l’un dépendant de l’autre. Encapsuler ces aspects dans des objets séparés permet de les utiliser et les laisser évoluer de manière indépendante.
-
Dès que le changement d’un objet entraîne le changement de plusieurs autres.
-
Dès qu’un objet doit en notifier un certain nombre d’autres sans les connaitre.
1.4. Observer en Java
Java fournit des classes Observable/Observer pour le patron Observer.
La classe java.util.Observable est la classe de base pour les sujets.
Ainsi, toute classe qui veut être observée étant cette classe dont
voici les caractéristiques :
-
fournit des méthodes pour ajouter/enlever des observateurs
-
fournit des méthodes pour notifier les observateurs
-
une sous-classe concrète doit seulement s’occuper de notifier à chque méthode modifiant l’état des objets (mutators)
-
utilise un vecteur stoquant les références des observateurs
L’interface java.util.Observer correspond aux observateurs
qui doivent implémenter cette interface.
1.4.1. La classe java.util.Observable
Voici la liste des méthodes de java.util.Observable :
1 public Observable()
2 public synchronized void addObserver(Observer o)
3 protected synchronized void setChanged()
4 public synchronized void deleteObserver(Observer o)
5 protected synchronized void clearChanged()
6 public synchronized boolean hasChanged()
7 public void notifyObservers(Object arg)
8 public void notifyObservers()
1.4.2. L’interface java.util.Observer
1 /**
2 * This method is called whenever the observed object is changed. An
3 * application calls an observable object's notifyObservers method to have all
4 * the object's observers notified of the change.
5 *
6 * Parameters:
7 * o - the observable object
8 * arg - an argument passed to the notifyObservers method
9 */
10 public abstract void update(Observable o, Object arg)
2. Une implémentation du MVC : les JTable java
2.2. L’architecture
3. Le patron Adaptateur
3.1. Le problème
On veut pouvoir :
-
utiliser une classe existante, mais dont l’interface ne coïncide pas avec celle escomptée.
-
créer une classe réutilisable qui collabore avec des classes sans relations avec elle et encore inconnues, c’est-à-dire avec des classes qui n’auront pas nécessairement des interfaces compatibles.
-
vous avez besoin d’utiliser plusieurs sous-classes existantes, mais l'adaptation de leur interface par dérivation de chacune d’entre elles est impraticable. Un adaptateur objet peut adapter l’interface de sa classe parente.
Ce dernier cas ne concerne que le cas "adaptateur d’objet"
3.2. Exemple concret : le retour des canards
-
L’existant :
public interface Canard {
public void cancaner();
public void voler();
}
public class Colvert implements Canard {
public void cancaner() {
System.out.println("Coincoin");
}
public void voler() {
System.out.println("Je vole");
}
}
-
Le "presque canard" :
public interface Dindon {
public void glouglouter();
public void voler();
}
public class DindonSauvage implements Dindon {
public void glouglouter() {
System.out.println("Glouglou");
}
public void voler() {
System.out.println("Je ne vole pas loin");
}
}
Vous êtes à court d’objets Canard et vous aimeriez utiliser des objets Dindon à la place!
public class AdaptateurDindon implements Canard {
Dindon dindon;
...
public void cancaner() {
dindon.glouglouter();
}
public void voler() {
// Adaptation du vol
for(int i=0; i < 5; i++) {
dindon.voler();
}
}
}
3.3. Le patron Adaptateur
Adaptateur (Adaptor) permet de convertir l’interface d’une classe en une autre conformément à l’attente du client. L’Adaptateur permet à des classes de collaborer, alors qu’elles n’auraient pas pu le faire du fait d’interfaces incompatibles.
4. Le patron Visiteur
4.1. Le problème
Quelques situations à problème :
-
Une structure d’objets contient beaucoup de classes différentes d’interfaces distinctes, et vous désirez réaliser des opérations sur ces objets qui dépendent de leurs classes concrètes.
-
Il s’agit d’effectuer plusieurs opérations distinctes et sans relation entre elles, sur les objets d’une structure, et ceci en évitant de polluer leurs classes avec ces opérations.
-
Les classes qui définissent la structure objet changent rarement, mais on doit souvent définir de nouvelles opérations sur cette structure.
4.2. Le patron Visiteur
Visiteur (Visitor) permet la représentation d’une opération applicable aux éléments d’une structure d’objet.
Il définit une nouvelle opération, sans qu’il soit nécessaire de modifier la classe des éléments sur lesquels elle agit.
4.3. Avantages/Inconvénients
Avantages :
-
Permet d’ajouter des opérations à la structure d’un Composite sans modifier la structure elle-même.
-
L'ajout de nouvelles opérations est relativement facile.
-
Le code des opérations exécutées par le Visiteur est centralisé.
Inconvénients :
-
L’encapsulation des classes du Composite est brisée.
-
Comme une fonction de navigation est impliquée, les modifications de la structure du Composite sont plus difficiles.
4.4. Exemples d’utilisation
-
calcul sur un ensemble structuré d’éléments
-
génération de rapports ou de code
-
…
4.5. Exemple concret d’utilisation en Java
|
|
Exemple tiré de ce site. |
public interface ItemElement {
public int accept(ShoppingCartVisitor visitor);
}
public class Book implements ItemElement {
private int price;
private String isbnNumber;
public Book(int cost, String isbn){
this.price=cost;
this.isbnNumber=isbn;
}
public int getPrice() {
return price;
}
public String getIsbnNumber() {
return isbnNumber;
}
@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
public class Fruit implements ItemElement {
private int pricePerKg;
private int weight;
private String name;
public Fruit(int priceKg, int wt, String nm){
this.pricePerKg=priceKg;
this.weight=wt;
this.name = nm;
}
public int getPricePerKg() {
return pricePerKg;
}
public int getWeight() {
return weight;
}
public String getName(){
return this.name;
}
@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
public interface ShoppingCartVisitor {
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
@Override
public int visit(Book book) {
int cost=0;
//apply 5$ discount if book price is greater than 50
if(book.getPrice() > 50){
cost = book.getPrice()-5;
} else cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
@Override
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
}
}
public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{new Book(20, "1234"),new Book(100, "5678"),
new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items){
sum = sum + item.accept(visitor);
}
return sum;
}
}
Book ISBN::1234 cost =20 Book ISBN::5678 cost =95 Banana cost = 20 Apple cost = 25 Total Cost = 160
5. Pour aller plus loin avec les patrons…
5.1. Partagez votre vocabulaire
-
Dans les réunions de conception (pas nécessairement avec le client)
-
Avec les autres développeurs
-
Dans la documentation de votre architecture
-
Dans les commentaires du code et les conventions de nommage
-
Dans les groupes/blogs de développeurs
-
(pas pendant les exams!)
5.2. Ne foncez pas tête baissée
Quelques conseils :
-
Les patterns sont des outils, non des règles.
⇒ Rien n’empêche de les modifier et de les adapter à votre problème.
-
Ne visez l’extensibilité que si la question se pose réellement dans la pratique, pas si elle est uniquement hypothétique.
-
Ne vous emballez pas et recherchez la simplicité.
⇒ Si vous trouvez une solution plus simple que l’emploi d’un pattern, n’hésitez pas !
-
Éliminez ce qui n’est pas vraiment nécessaire.
⇒ N’ayez pas peur de supprimer un design pattern inutile de votre conception.
5.3. Les autres types de patrons
Il n’y a pas que les 3 types de patrons que l’on a vu :
-
De création
-
Structurels
-
Comportementaux
Il y a par exemple :
-
Les patrons d’architecture
-
Les patrons d’application
-
Les patrons de domaine
-
Les patrons de processus
-
Les patrons d’organisation
-
Les patrons de conception d’interfaces utilisateur
5.4. Les anti-patrons
-
Des solutions souvent appliquées à tort à des problèmes récurrents
-
Décrit comment partir d’un problème pour arriver à une mauvaise solution
-
Vous dit pourquoi une mauvaise solution est attrayante
-
Suggère d’autres patrons applicables pouvant fournir de meilleures solutions
5.5. Tous les patrons qu’on a pas vu
Il y en a beaucoup :
-
Chaîne de responsabilité
-
Commande
-
Décorateur
-
Façade
-
Interprète
-
Médiateur
-
Mémento
-
Monteur
-
Patron de méthode
-
Poids-mouche
-
Pont
-
Prototype
5.6. Principale utilisation des patrons : refactoring!
⇒ Les 2 prochaines semaines vous allez refactorer une application :
-
en binôme
-
en testant
-
en documentant
-
en justifiant
-
en essayant pas à tout prix de caser le plus de patrons "au kilo"